package fiuba.algo3;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class Mapa {
	static final int ULTIMA_CIUDAD_CON_PISTA = 4;
	static final int CANTIDAD_DE_CIUDADES = 3;
	ArrayList<Ciudad> rutaDeEscape;
	Ciudad ciudadActual;
	Ciudad ultimaVisitada;
	Ciudad proximaCiudad;
	int indice;
	
	public Mapa (ArrayList<Ciudad> rutaDeEscape, Ciudad ciudadDelRobo) {
		this.rutaDeEscape = rutaDeEscape;
		this.ciudadActual = ciudadDelRobo;
		this.ultimaVisitada = ciudadDelRobo;
		this.proximaCiudad = this.getProximaCiudad(this.ciudadActual);
		this.indice = 0;
	}
	
	public Mapa() {}
	
	public Node serializar(Document doc) {
		Element elementMapa = doc.createElement("Mapa");
		elementMapa.appendChild(this.ciudadActual.serializar(doc));
		elementMapa.appendChild(this.ultimaVisitada.serializar(doc));
		if (this.proximaCiudad != null)
			elementMapa.appendChild(this.proximaCiudad.serializar(doc));
		elementMapa.setAttribute("indice", Integer.toString(this.indice));
		Element elementoRutaDeEscape = doc.createElement("RutaDeEscape");
		elementMapa.appendChild(elementoRutaDeEscape);
		for (Ciudad ciudad : this.rutaDeEscape) {
			elementoRutaDeEscape.appendChild( ciudad.serializar(doc) );
		}
		return elementMapa;
	}

	public static Mapa hidratar(Node elementoMapa) {
		Mapa nuevoMapa = new Mapa();
		nuevoMapa.indice = Integer.parseInt( ((Element)elementoMapa).getAttribute("indice") );
		nuevoMapa.ciudadActual = Ciudad.hidratar( ((Element) elementoMapa).getElementsByTagName("Ciudad").item(0) );
		try{
			nuevoMapa.proximaCiudad = Ciudad.hidratar( ((Element) elementoMapa).getElementsByTagName("Ciudad").item(0) );
		}
		catch (NullPointerException e){
			nuevoMapa.proximaCiudad = null;
		}
		nuevoMapa.ultimaVisitada = Ciudad.hidratar( ((Element) elementoMapa).getElementsByTagName("Ciudad").item(0) );		
				
		ArrayList<Ciudad> rutaDeEscape = new ArrayList<Ciudad>();
		Element elementoRutaDeEscape =  (Element) ((Element) elementoMapa).getElementsByTagName("RutaDeEscape").item(0);
		for(int i=0; i< elementoRutaDeEscape.getChildNodes().getLength(); i++) {
			Ciudad ciudad = Ciudad.hidratar(elementoRutaDeEscape.getChildNodes().item(i));
			rutaDeEscape.add(ciudad);
		}
		nuevoMapa.rutaDeEscape = rutaDeEscape;
		
		return nuevoMapa;	
	}
	
	public Ciudad proximaCiudad() {
		return this.proximaCiudad;
	}
	
	public ArrayList<Ciudad> generarMapa() {

		ArrayList<Ciudad> ciudadesDeMapa = new ArrayList<Ciudad>();
		// Muestro siempre la proxima ciudad correcta en el mapa
		ciudadesDeMapa.add( this.proximaCiudad);
		
		NodeList listaCiudades = new ManejadorXML().obtenerElementosDeXML( "ciudades.xml", "ciudad" );
		
		if (listaCiudades != null){			
			while (ciudadesDeMapa.size() != CANTIDAD_DE_CIUDADES){
			
				// Creo una ciudad aleatoria
				int i = new Double(Math.random() * (listaCiudades.getLength())).intValue();
				Element elementoCiudad = (Element) listaCiudades.item(i);
	    		Ciudad posibleCiudad = new Ciudad (elementoCiudad.getAttribute("nombre"));
				
	    		boolean seRepite = false;
	    		
				// NO se pueden repetir ciudades
				seRepite = verificarMapaCorrecto(ciudadesDeMapa, posibleCiudad);

				// NO tiene que estar la ciudad actual
				boolean noEsCiudadActual = !posibleCiudad.esIgualA(this.ciudadActual);

	        	// NO tiene que estar la ultima!
				boolean noEsUltimaCiudad = !posibleCiudad.esIgualA(this.rutaDeEscape.get(this.rutaDeEscape.size()-1));
				
				if (!seRepite && noEsCiudadActual && noEsUltimaCiudad ){
	        		ciudadesDeMapa.add(posibleCiudad);
	        	}	
	        }
		}
		// Mezclo las ciudades
		Collections.shuffle(ciudadesDeMapa, new Random(System.nanoTime()));
		return ciudadesDeMapa;
	}
	
	private boolean verificarMapaCorrecto(ArrayList<Ciudad> ciudades, Ciudad posibleCiudad) {
		for ( int j = 0; j < ciudades.size(); j++ ){
			Ciudad opcionCiudad = ciudades.get(j);
			if ( opcionCiudad.esIgualA(posibleCiudad) ) { return true; }
		}
		return false;
	}
	
	public boolean ciudadRecibePistaLadron() {
		if (this.obtenerPosicionEnLaRutaDeEscape() <= ULTIMA_CIUDAD_CON_PISTA){
			return true;
		}
		return false;
	}
		
	public Ciudad getProximaCiudad(Ciudad ciudadActual) {
        for ( int j = 0; j < this.rutaDeEscape.size(); j++ ){
        	Ciudad opcionCiudad = this.rutaDeEscape.get(j);
        	int ultimoElemento = this.rutaDeEscape.size() - 1;
        	if ( opcionCiudad.esIgualA(ciudadActual) && (opcionCiudad.esIgualA(this.rutaDeEscape.get(ultimoElemento)) == false) ){
        		j = j + 1;
        		return this.rutaDeEscape.get(j);
        	}
        }
		return null;
	}
	
	public boolean esUltimaCiudad(Ciudad ciudad) {
		return (this.ciudadActual.esIgualA(this.rutaDeEscape.get(this.rutaDeEscape.size() - 1)));
	}
	
	public int obtenerPosicionEnLaRutaDeEscape() {
		int indice = 0;
		for (int i = 0; i < this.rutaDeEscape.size(); i++){
			if (this.rutaDeEscape.get(i).esIgualA(this.ciudadActual))
				indice = i;
		}
		return indice;
	}
	
	public boolean acertoCiudad(Ciudad ciudadUsuario) {
		Ciudad proximaCiudad = this.getProximaCiudad(this.ultimaVisitada);

		// si la ciudad propuesta es la que sigue en la ruta o ya viaje y estoy chequeando para
		// crear los edificios
		// es true
		// si es realmente solo la correcta entonces actualizo referencias
		if (ciudadUsuario.esIgualA(proximaCiudad) || (ciudadUsuario.esIgualA(this.ultimaVisitada)) ){
			if (ciudadUsuario.esIgualA(this.ultimaVisitada) == false)
				this.ciudadActual = ciudadUsuario;
				this.ultimaVisitada = ciudadUsuario;
				this.proximaCiudad = this.getProximaCiudad(this.ciudadActual);
			return true;
		}
		this.ciudadActual = ciudadUsuario;
		return false;
	}

}
